﻿// Inner Fire 游戏引擎库
// Animator - 动画状态机
//
// Copyright (c) 2025 by 尔西大帝. All rights reserved.
//
// 所属层级：应用层(4)
// 用于管理游戏中的动画状态。
// 这个Animator的设计理念和PlayableGraph一样，当时我做了一个PlayableGraph，
// 突然想着，为什么这两个要分开呢？在Unity里面之所以分开，是因为他先做了Animator，
// 然后觉得这东西落后，后来在框架中又增加PlayableGraph，这是为了兼容性的原因
// 这里就体现出了一个长线运营的引擎所面临的问题——为了适配以前的用户，你只能不停地加
// 而不能改变原来的框架，这样就导致系统越来臃肿。
//
// Author: 尔西大帝
// Email: 2670613871@qq.com
// Created: 2025-04-24

#pragma once

#include <game/animation/animation_clip.h>
#include <game/animation/animation_def.h>
#include <game/animation/animator_channel.h>
#include <game/animation/animator_printer.h>
#include <game/animation/armature.h>
#include <game/animation/humanoid.h>
#include <game/component/component.h>
#include <game/animation/animator_events.h>

namespace ifire::game {
class InstancedSpriteObject;
class PhysicsBone;

// 用于处理获得什么样的信息
constexpr int ARMATURE_STATIC_POSE = 0;   // 模型的原生Pose
constexpr int ARMATURE_STATIC_AVATAR = 1; // Avatar所使用的T-Pose

// 动画的播放形式，仅用于调试
enum class AnimatorExecuteMethod {
  Normal,        // 正常播放动画
  Pose,          // 播放一个Pose
  Avatar,        // 播放一个Avatar
  HumanoidFrame, // 播放Humanoid帧（目前用于测试）
  Raw,           // 不进行任何的骨骼变换
};

class Animator : public Component {
public:
  Animator(GameObject* game_object);
  Animator(const Animator& lhs) = delete;
  Animator& operator=(const Animator& lhs) = delete;
  ~Animator() override;

  friend class AnimatorPrinter;
  friend class Humanoid;
  friend class PhysicsBone;

  // 设置一个动画
  void Play(AnimationClip* clip, PlayMethod loop, float transition = 0.0f);
  // 播放一个Blend动画
  void PlayBlend(AnimationBlend* blend, PlayMethod loop, float transition = 0.0f);

  // 设置只播放Pose（即清除动画）
  void PlayPose();
  // 播放Avatar（将项目定位到Avatar的初始姿势）
  void PlayAvatar();
  // 播放Pose为Raw（即所有Mesh根本不产生变换）
  void PlayRaw();

  // 获得主CLIP，也就是当前播放的重点，用于一些Attacher的计算等。
  inline const AnimationClip* GetMainClip() { return main_clip_; }


  // 设置播放动画的其中一帧
  void PlayFrame(AnimationClip* clip, float frame);

  // 设置一个Debug的实例精灵。设置骨骼的信息
  void SetSpriteInformation(ikit::sstr type, InstancedSpriteObject* obj);

  // 设置动画的值
  void SetValue(float x, float y);
  // 设置是否使用RootMotion
  void UseRootMotion(bool value);
  // 是否使用RootMotion
  bool IsRootMotion() const;
  // 返回RootMotion的Delta
  DirectX::XMFLOAT3 GetRootMotionDelta() const;
  // 得到骨架
  Armature* GetArmature() { return armature_; }
  // 得到Humanoid计算器
  Humanoid* GetHumanoid() { return humanoid_.get(); }
  // 得到事件管理器
  AnimatorEvents* GetEvents() { return events_.get(); }

  // 得到动画机
  dx::SkinAnimator* GetSkinAnimator() const;
  // 每帧更新
  void FixedUpdate(float delta_time) override;
  // 延迟更新
  void LateUpdate(float delta_time) override;
  // 获得静态姿势
  void GetStaticPose(MatrixList& transforms, int type);
  // 绑定PhysicsBone
  void SetPhysicsBone(PhysicsBone* physics_bone);

  // 获得这个骨骼在指定动画项目的姿势
  void SetAnimeFrame(Snapshot& snapshot);

  // 获得此项目的类型
  static ikit::sstr ComponentType();
  // 获得Meta名称
  const char* GetMetaName() override;

  // 增加一个Channel
  AnimatorChannel* AddChannel(ikit::sstr name);

  // 获得一个Bone还没有被计算逆绑定矩阵之前的矩阵
  FMat GetBoneFinalMatrix(int bone_id);
  // 获得一个Bone的最终矩阵
  FMat4 GetBoneRenderMatrix(ikit::sstr name);
  // 获得一个Bone的最终矩阵
  FMat4 GetBoneRenderMatrix(int bone_id);
  // 获得一个Bone的分解项目
  Trans3 GetBoneTransform(ikit::sstr name);

  // 打印项目
  void Print(ikit::sstr name);

  // 获得一个Parent的项目
  inline IMat GetParentMat(ArmatureNode* node) {
    return node ? parents_[node->index] : ID_MAT;
  }
  // 设置一个Parent项目
  inline void SetParentMat(int index, FMat m) { parents_[index] = m; }

public:
  // 从Update获得的DeltaTime，其它的计算都引用这个
  float deltaTime;

private:
  // 处理Pose播放
  void OnPose();
  // 处理Avatar播放
  void OnAvatar();
  // 处理一般播放
  void OnNormal(float delta_time);
  // 处理Humanoid播放
  void OnHumanoid();

  // 用于测试，获得Channel0的主矩阵
  MatrixList& PrimaryList();

  // 此动画机所对应的骨架
  Armature* armature_;
  // 此动画的蒙皮骨骼动画机
  dx::SkinAnimator* skin_animator_;
  // 当前动画机的Channel
  eastl::vector<AnimatorChannel*> channels_;
  // 默认Channel的快捷方式
  AnimatorChannel* channel0_;
  // 用于计算Humanroid
  std::unique_ptr<Humanoid> humanoid_;
  std::unique_ptr<AnimatorPrinter> printer_;
  // 播放的方式
  AnimatorExecuteMethod method_;
  // 当前播放的主Clip
  const AnimationClip* main_clip_;
  // 当前播放的主Clip的时间
  float main_clip_tick_;

  // 用于保存Parent的计算结果，在很多地方有用。
  // 即便是使用多线程，也是每个Animator一个线程。所以放在这里
  MatrixList parents_;
  // 用于保存最终的变换矩阵
  MatrixList final_mats_;

  // 用于动态骨骼
  PhysicsBone* physics_bone_;

  // 用于Events的管理
  std::unique_ptr<AnimatorEvents> events_;

  // 临时项目
  const AnimationClip* humanoid_clip_;
  float humanoid_frame_;
  // 是否使用RootMotion
  bool use_root_motion_;
};

} // namespace ifire::game